<#assign object = doc.object>
package ${object.package}.model.base;

<#if object.attributes.xml[0]??>
import ${object.package}.model.xml.*;
import org.apache.commons.lang.builder.HashCodeBuilder;
</#if>
<#if object.attributes.html[0]??>
import redora.rdo.html.HtmlHandler;
</#if>
<#list doc["/object/attributes/enum[@scope='global']"] as enum>
import ${object.package}.model.enums.${enum.@class};
</#list>

<#if object.attributes.set[0]?? || doc["/object/attributes/*[@lazy='true']"][0]??>
import redora.rdo.exceptions.*;
import ${object.package}.service.*;
</#if>
<#if object.attributes.set[0]??>
import redora.rdo.impl.*;
</#if>
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.text.ParseException;

import redora.rdo.api.*;
import redora.rdo.util.Constants;
import java.util.logging.Level;
import java.util.logging.Logger;
import ${object.package}.model.*;
import ${object.package}.model.fields.${object.name}Fields;

<#if object.description[0]??>
/** 
${object.description} 
*/
</#if>
public class ${object.name}Base implements Persistable<#if object.interface[0]??>, ${object.interface}</#if> {
	private Logger l = Logger.getLogger("${object.package}.model.${object.name}Base");
  
  	public Long id;
  	public Long getId() {
    	return id;
  	}
  	public void setId(Long id) {
   	if (this.id != null) throw new RuntimeException("It is not allowed to modify the id");
    	this.id = id;
  	}
  	public Date creationDate;
  	public Date getCreationDate() {
    	return this.creationDate;
  	}
  	public Date updateDate;
  	public Date getUpdateDate() {
    	return this.updateDate;
  	}
<#list object.attributes.enum as att>
	<#if !att.@scope[0]?? || att.@scope[0] == "local">
		<#if att.description[0]??>
  	/** ${att.description} */
		</#if>
  	public enum ${att.@class} {
		<#list att.element as value>
			<#if value.@description[0]??>
    	/** ${value.@description} */
			</#if>
    	${value.@name}<#if value_has_next>,<#else>;</#if>
		</#list>
    	/** @return Null, or the result of valueOf() */
    	public static ${att.@class} valueOfNullSafe(String value) {
	  		if (value != null)
				return valueOf(value);
	  		return null; 
    	}
  	}
	</#if>
</#list>

<#list object.formScope?children as att>
    <#if att?node_type == "element">
  	public ${att.@className} ${att.@fieldName};
    </#if>
	<#if att?node_name == "xml">
  	/** Reflection Hash, determined when ${att.@name} is retrieved from the database. When not yet retrieved, it's value equals -1 */
  	public int dirty${att.@fieldName?cap_first}Hash = -1;
  	</#if>
</#list>

<#list object.attributes.set as att>
	<#if att.@multiplicity == "n-to-1">
	private ${att.@className} ${att.@fieldName} = new PersistableHashSet<${att.@class}>();
	<#else>
	private ${att.@className} ${att.@fieldName} = new PersistableNtoMHashSet<${att.@class}>();
	</#if>
</#list>

  	public Map<${object.name}Fields, Object> dirty = new HashMap<${object.name}Fields, Object>();
  	public boolean isNew = true;
  	/** How is this object retrieved. By default (if for example the object is new) it's scope is assumed as Form. */
  	public FetchScope fetchScope = FetchScope.Form;

  	/**
  	* This method will set default values for:<br>
<#list doc["/object/attributes/*[@default]"] as att>
  	* ${att.@fieldName} - ${att.@default};<br>
</#list>
  	*/
  	public ${object.name}Base() {
    	super();
<#list doc["/object/attributes/*[@default]"] as att>
	<#if att?node_name == "boolean">
    	set${att.@fieldName?cap_first}(Boolean.${att.@default?upper_case});
	<#elseif att?node_name == "string">
    	set${att.@fieldName?cap_first}("${att.@default}");
	<#elseif att?node_name == "integer" || att?node_name == "long" || att?node_name == "double">
    	set${att.@fieldName?cap_first}(${att.@className}.valueOf(${att.@default}));
	<#elseif att?node_name == "enum">
    	set${att.@fieldName?cap_first}(${att.@className}.${att.@default});
    <#else>
    	error - undefined default ${att?node_name}
    </#if>
</#list>
  	}

<#list object.formScope?children as att>
	<#if att.description[0]??>
  	/** ${att.description} */
  	</#if>
  	public void set${att.@fieldName?cap_first}(${att.@className} ${att.@fieldName}) <#if att.@lazy == "true">throws LazyException <#if att?node_name == "html">, FieldException</#if><#elseif att?node_name == "html">throws FieldException</#if>{
  	<#if att?node_name == "date" || att?node_name == "datetime">
    	String stripped = null;
    	//strip (nano) seconds - hours to ${att?node_name} level
    	if (${att.@fieldName} != null) {
      		stripped = Constants.${att?node_name?upper_case}.format(${att.@fieldName});
    	}
    	if ((this.${att.@fieldName} == null ? null : Constants.${att?node_name?upper_case}.format(this.${att.@fieldName})) != stripped) {
      		if (!dirty.containsKey(${object.name}Fields.${att.@fieldName})) {
        		dirty.put(${object.name}Fields.${att.@fieldName}, this.${att.@fieldName});
      		}
      		if (stripped == null) {
        		this.${att.@fieldName} = null;
      		} else {
        		try {
          			this.${att.@fieldName} = Constants.${att?node_name?upper_case}.parse(stripped);
        		} catch (ParseException e) {
          			l.log(Level.SEVERE, "Did not expect this would not parse to date " + stripped, e);
          			throw new RuntimeException("Did not expect this would not parse to date " + stripped, e);
        		}
      		}
    	}
  	}
  	<#elseif att?node_name == "xml">
		//No need for any dirty setting, this is done just before persisting using dirty${att.@fieldName?cap_first}Hash.
    	this.${att.@fieldName} = ${att.@fieldName};
  	}
  	<#elseif att?node_name == "object">
    	if (get${att.@fieldName?cap_first}() != ${att.@fieldName} && !dirty.containsKey(${object.name}Fields.${att.@fieldName}Id)) {
      		dirty.put(${object.name}Fields.${att.@fieldName}Id, this.${att.@fieldName}Id);
    	}
    	this.${att.@fieldName} = ${att.@fieldName};
    	if (this.${att.@fieldName} != null) {
      		this.${att.@fieldName}Id = ${att.@fieldName}.getId();
    	} else {
      		this.${att.@fieldName}Id = null;
      	}
  	}
  	<#elseif att?node_name == "html">
    	if (${att.@fieldName} != null) {
      		String _cleanHtml = HtmlHandler.clean("${att.@policy[0]!"antisamy.xml"}", ${att.@fieldName});
      		if (_cleanHtml != get${att.@fieldName?cap_first}()) {
        		if (!dirty.containsKey(${object.name}Fields.${att.@fieldName})) {
          			dirty.put(${object.name}Fields.${att.@fieldName}, get${att.@fieldName?cap_first}());
        		}
        		this.${att.@fieldName} = _cleanHtml;
        	}
      	} else if (this.${att.@fieldName} != null) {
      		if (!dirty.containsKey(${object.name}Fields.${att.@fieldName})) {
        		dirty.put(${object.name}Fields.${att.@fieldName}, this.${att.@fieldName});
      		}
      		this.${att.@fieldName} = null;
    	}
  	}
  	<#elseif att?node_type == "element">
    	if (${att.@fieldName} != get${att.@fieldName?cap_first}() && !dirty.containsKey(${object.name}Fields.${att.@fieldName})) {
      		dirty.put(${object.name}Fields.${att.@fieldName}, this.${att.@fieldName});
    	}
    	this.${att.@fieldName} = ${att.@fieldName};
  	}
  	</#if>

  	<#if att.description[0]??>
  	/** ${att.description} **/
  	</#if>
  	public ${att.@className} get${att.@fieldName?cap_first}() <#if att.@lazy == "true">throws LazyException </#if>{
  	<#if att.@lazy == "true">
  		<#if att?node_name == "object">
     	if (${att.@fieldName}Id != null) {
     	</#if>
     	if (fetchScope == FetchScope.Table) {
       		fetchLazy();
	    <#if att?node_name == "xml">
		} else if (${att.@fieldName} == null && fetchScope == FetchScope.Form) {
	    	${att.@fieldName} = new ${att.@className}();
    		dirty${att.@fieldName?cap_first}Hash = 0;
    	<#elseif att?node_name == "object">
     	}
     	</#if>
     	}
    <#elseif att?node_name == "xml">
    	${att.@fieldName} = new ${att.@className}();
    	dirty${att.@fieldName?cap_first}Hash = 0;
    </#if>
		return ${att.@fieldName};
	}
</#list>

<#list object.attributes.set as att>
	/**
  <#if att.description[0]??>
	* ${att.description}
  </#if>
	* <br />
	* If you wish to add a ${att.@class} to this list, do something like: get${att.@class}s().add(${att.@class});.
	* All the Collection methods like add, remove, addAll and removeAll are supported.
	* When you add or update ${att.@class}s to this set, they will be persisted together with ${object.name}.
	* @return Empty or filled list.
	*/
	public ${att.@className} get${att.@fieldName?cap_first} ()
	    	throws ConnectException, QueryException, CopyException {
    	if (!${att.@fieldName}.isRetrieved() && !isNew) {
	      	${att.@fieldName}.setRetrieved(true);
    	  	${att.@fieldName}.addAll(ServiceFactory.${att.@class?uncap_first}Service().findBy${object.name}Id(id, FetchScope.Table));
    	}
    	
    	return ${att.@fieldName};
  	}
</#list>
	
	/**
	* Checks if any of the attributes have changed. Related children are not checked.
	* <#if object.attributes.xml[0]??>Changes in the XML document are found using reflection on the document.</#if>
	* @return true if any of the attributes of ${object.name} have changed
	*/
	public boolean isDirty() {
	<#list object.attributes.xml as att>
		//JAXB XML documents cannot be compared using .equals. The XML document has to be compared element by element.
		//Reflection is used to compare two XML documents. When ${att.@fieldName} is retrieved from storage, the old${att.@fieldName?cap_first}Hash is set.
		//So, to see if the XML document is changed compare it with the current hash.
		if (dirty${att.@fieldName?cap_first}Hash != -1 || ${att.@fieldName} != null) { //Meaning: ${att.@fieldName} is retrieved from storage or has been initialized
			try {
				int new${att.@fieldName?cap_first}Hash = HashCodeBuilder.reflectionHashCode(get${att.@fieldName?cap_first}());
				if ((get${att.@fieldName?cap_first}() == null && dirty${att.@fieldName?cap_first}Hash != 0) || (get${att.@fieldName?cap_first}() != null && new${att.@fieldName?cap_first}Hash != dirty${att.@fieldName?cap_first}Hash)) {
					dirty.put(${object.name}Fields.${att.@fieldName}, this.${att.@fieldName});
					if (get${att.@fieldName?cap_first}() == null)
						dirty${att.@fieldName?cap_first}Hash = 0;
					else
						dirty${att.@fieldName?cap_first}Hash = new${att.@fieldName?cap_first}Hash;
				}
			} catch (PersistenceException e) {}
		}
	</#list>
	
		return dirty.size() != 0;
 	}
	
	/** 
	* Persistence Layer's method, so you will probably will not need to use this method. 
	* Sets this ${object.name} as if everything is
	* persisted. Any related objects and children are left unchanged.
	*/
    public void clean() {
    	dirty = new HashMap<${object.name}Fields, Object>();
    }

    public boolean equals(${object.name} o) {
    	if (getId() != null)
        	return getId().equals(o.getId());
      	return false;
    }
  
<#if doc["/object/attributes/*[@lazy='true']"][0]??>
    private void fetchLazy() throws LazyException {
	  	//Set fetchScope at the beginning, otherwise you migh get an infinite loop with get/set thinking it is still lazy 
	  	fetchScope = FetchScope.Form;
	  	
    	try {
	    	Map<String, Object> _lazy = ServiceFactory.${object.name?uncap_first}Service().fetchLazy(id);
	<#list object.lazyScope?children as att>
      	<#if att?node_name == "xml">
      		${att.@fieldName} = (${att.@className})_lazy.get("${att.@fieldName}");
      		if (${att.@fieldName} != null) {
		  		dirty${att.@className}Hash = HashCodeBuilder.reflectionHashCode(${att.@fieldName});
			} else {
		  		dirty${att.@className}Hash = 0;
		  		${att.@fieldName} = new ${att.@className}();
		  	}
    	<#elseif att?node_type == "element" && att?node_name != "object">
        	${att.@fieldName} = (${att.@className})_lazy.get("${att.@fieldName}");
    	</#if>
	</#list>
	<#list object.lazyScope.object as att>
			if (${att.@fieldName}Id != null) {
				set${att.@fieldName?cap_first}(ServiceFactory.${att.@className?uncap_first}Service().findById(${att.@fieldName}Id, FetchScope.Table));
			}
	</#list>
	  	} catch (PersistenceException e) {
	    	throw new LazyException("Failed to retrieve lazy fetched attributes for " + id, e);
	  	}
    }
</#if>
}
